#include "fxcc/core/graph/opengl3/ImGuiGlfwApp.h"
#include "fxcc/core/graph/common/Geometry.h"
#include "fxcc/core/graph/opengl3/TextureCubeJack.h"
#include "fxcc/core/graph/opengl3/RenderTargetCube.h"

#include "fxcc/imgui/Editor.h"
#include "fxcc/core/AssertManager.h"

using namespace fxcc::platform;
using namespace fxcc::graph;
using namespace fxcc::graph::opengl3;
using namespace fxcc::imgui::graph::opengl3;
using BaseApp = fxcc::imgui::graph::opengl3::GlfwApp;

#undef main

struct SimpleApp : public BaseApp
{
public:
    std::shared_ptr<fxcc::graph::opengl3::RenderTargetJack> m_RenderTarget;
    std::shared_ptr<RenderTargetCube> m_RTCube;

    fxcc::graph::common::Transform transform;
    glm::vec4 color;
    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_Effect;
    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_Effect2;
    std::shared_ptr<fxcc::graph::opengl3::MeshJack> m_CubeMesh;
    fxcc::graph::common::Camera camera;
    std::shared_ptr<fxcc::graph::opengl3::Texture2DJack> m_TextureHDR;

    SimpleApp(const fxcc::platform::common::App::Desc& desc) : BaseApp(desc), color(1.0)
    {
    }
    bool Init() override
    {
        if (!BaseApp::Init())
        {
            return false;
        }

        if (!InitResources())
        {
            return false;
        }

        return true;
    };

    void printOpenGLESVersion()
    {
        const char* version = (const char*)glGetString(GL_VERSION);
        std::cout << "OpenGL ES Version: " << version << std::endl;
    };

    bool InitResources()
    {
   

        camera.SetPosition(glm::vec3(5.0f));
        camera.LookAt(glm::vec3(0.0f));
        const std::string fragCode = R"(
out vec4 FragColor;

uniform samplerCube environmentMap;

void main()
{
   vec3 envColor = textureLod(environmentMap, localPos, 0.0).rgb;
    
    // HDR tonemap and gamma correct
    // envColor = envColor / (envColor + vec3(1.0));
    // envColor = pow(envColor, vec3(1.0/2.2)); 
    
    FragColor = vec4(envColor, 1.0);
}
)";
        const std::string hdrPath = fxcc::AssertManager::GetAbsoluteFile("hdr/qwantani_dusk_2_4k.hdr");
        fxcc::graph::common::Texture2DJack::Desc texDesc;
        texDesc.SetWIC(hdrPath, 4, 2, true);
        m_TextureHDR = std::make_shared<fxcc::graph::opengl3::Texture2DJack>(texDesc);


        m_Effect = std::make_shared<fxcc::graph::opengl3::Effect3d>(fragCode);
        m_Effect->m_PipeLineType.m_RasterizerType = fxcc::graph::common::RasterizerType::_NONE_;

        m_CubeMesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::CreateBox());


        m_Effect2 = std::make_shared<fxcc::graph::opengl3::Effect3d>(
        
        R"(

out vec4 FragColor;
uniform sampler2D equirectangularMap;

const vec2 invAtan = vec2(0.1591, 0.3183);
vec2 SampleSphericalMap(vec3 v)
{
    vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
    uv *= invAtan;
    uv += 0.5;
    return uv;
};

void main()
{		
    vec2 uv = SampleSphericalMap(normalize(localPos));
    vec3 color = texture(equirectangularMap, uv).rgb;
    
    FragColor = vec4(color, 1.0);
}


)"
        );
        m_Effect2->m_PipeLineType.m_RasterizerType = fxcc::graph::common::RasterizerType::_NONE_;

        InitRTTCube();

        return true;
    };

    fxcc::imgui::Editor editor;
    void InitRTTCube()
    {
        RenderTargetCube::Desc rtCubeDesc;
        rtCubeDesc.m_FaceMipmapData = { 1024,1024,4,2 };
        m_RTCube = std::make_shared<RenderTargetCube>(rtCubeDesc);

        m_Effect2->Begin();
        m_Effect2->SetFixed();

        for (int i = 0; i < 6; i++)
        {
            m_RTCube->Begin(i);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            Effect3d::PassData passData;
            passData.m_Proj = fxcc::graph::common::TextureCubeJack::captureProjection;
            passData.m_View = fxcc::graph::common::TextureCubeJack::captureViews[i];
            passData.m_ProjView = passData.m_Proj * passData.m_View;
            m_Effect2->SetPassData(passData);
            m_Effect2->SetObjData(fxcc::graph::common::Transform());
            
            m_Effect2->m_Shader->setInt("environmentMap", 0);
            m_TextureHDR->Active(0);
            m_CubeMesh->Bind();
            m_CubeMesh->DrawElementsAuto();
            m_RTCube->End();
        }
        m_Effect2->End();

        //m_RTCube->m_TextureCube->CreateFaceImages("test_{{{index}}}.hdr", false);
        m_RTCube->m_TextureCube->GenerateFaceTexture2D();
    }
    void OnEdit() override
    {
        ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());

        ImGui::Begin("Hello world");
        editor.AddFrameBuffer(m_RenderTarget->m_Textures[0]->GetTextureId());
        ImGui::End();

        ImGui::Begin("Texture Cube");
        for (int i = 0; i < 6; i++)
        {
            const std::string& name = fxcc::graph::common::TextureCubeJack::CubeFaceName[i];

            auto tex = m_RTCube->m_TextureCube->m_FaceTextures[i];
            if (tex)
            {
                ImGui::Text(name.c_str());
                ImGui::Image(tex->GetTextureId(), { 200,200 });
            }
        }
        ImGui::End();

        
    }

    void OnDestroy() override
    {
        BaseApp::OnDestroy();
    }

    void OnFrameRender() override
    {
        glViewport(0, 0, m_Desc.m_Size.x, m_Desc.m_Size.y);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        InitRTTCube();

        //for (int i = 0; i < 6; i++)
        //{
        //    auto rt = m_FaceRT[i];
        //    rt->Begin();
        //    glClearColor(color.x, color.y, color.z, color.w);
        //    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        //    m_CubeFaceEffect->Begin();
        //    //m_CubeFaceEffect->SetCamera(camera);
        //    Effect3d::PassData passData;
        //    passData.m_Proj = fxcc::graph::common::TextureCubeJack::captureProjection;
        //    passData.m_View = fxcc::graph::common::TextureCubeJack::captureViews[i];
        //    passData.m_ProjView = passData.m_Proj * passData.m_View;

        //    m_CubeFaceEffect->m_Shader->Use();
        //    m_CubeFaceEffect->m_Shader->setInt("cubeMap", 0);
        //    m_CubeFaceEffect->m_Shader->setInt("gamma", m_TextureCubeJack->m_Desc.m_HDRConfig.m_HDR);
        //    m_TextureCubeJack->Active(0);

        //    m_CubeFaceEffect->SetPassData(passData);
        //    m_CubeFaceEffect->SetObjData(transform);
        //    m_CubeFaceEffect->InstMeshJack(m_CubeMesh.get());
        //    m_CubeFaceEffect->End();

        //    rt->End();
        //}

    };

    void OnFramebuffersize(int w, int h) override
    {
        opengl3::GlfwApp::OnFramebuffersize(w, h);
        camera.SetAspect(m_Desc.Aspect());

        m_RenderTarget = std::make_shared<fxcc::graph::opengl3::RenderTargetJack>(fxcc::graph::common::RenderTargetJack::Desc(1, { w,h, 4, 1 }));
    }
    void OnUpdate() override
    {
        opengl3::GlfwApp::OnUpdate();

        float ctTime = m_GlfwlTimer.m_CurrentTime;
        camera.FreeMove(m_Input, m_GlfwlTimer.m_DeltaTime);

        float deltaTime = m_GlfwlTimer.m_DeltaTime;
    }
};

int main()
{
    ztclog::pushFunction([](const ztclog::Line& line)
        {
            {
                std::cout << line.m_Text << std::endl;
            } });

            using _App = SimpleApp;
            using _Desc = fxcc::platform::common::App::Desc;

            _Desc desc;
            desc.m_Pos = glm::ivec2(100, 100);
            desc.m_Size = glm::ivec2(900, 600);
            desc.m_ScaleFactor = 1.0f;

            desc.m_Title = "test font";
            std::shared_ptr<_App> _app = std::make_shared<_App>(desc);
            return _app->Run();
};
